package com.cy.ppj.comm.third.tg;
/**
 * 数字签名、验证工具类
 */

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.lang3.StringUtils;

public class Cryptor {
    	
	///////////////// 外部调用api函数入口 begin ////////////////////////////////////////
    
	/**
	 * 签名函数入口
	 * message:  消息明文
	 * keyFile:  私钥文件，自己的证书文件，一般为.pfx结尾
	 * password: 私钥文件密码 
	 * @throws UnsupportedEncodingException 
	 */
	public static String sign(String message, String keyFile, String password) 
	throws InvalidKeyException, SignatureException, UnsupportedEncodingException {
		try { 
			PrivateKey key = getPrivateKey(keyFile, password); 
			String alg = "MD5withRSA";
			byte[] msg = string2Bytes(message);
			byte[] out = sign(alg, key, msg); 
			return bytesToHexString(out).toLowerCase();
		}
		catch(NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/** 
     * 验证签名函数入口
	 * certFile: 证书文件，对方的公钥证书文件，一般为.cer结尾
	 * message:  消息明文
	 * signature: 需要验证的消息数字签名
     * @throws UnsupportedEncodingException 
	*/
	public static boolean verify(String certFile, String message, String signature) 
	throws CertificateException, UnsupportedEncodingException {
		String alg = "MD5withRSA";
		Certificate cert = createCertificate(certFile);
		byte[] msg = string2Bytes(message);
		return verify(alg, cert, msg, hexStringToBytes(signature));
	}
 
	////////////////// 外部调用api函数入口 end //////////////////////////////////////////
	
	//MD5算法
    
    
	/*
	 * 签名
	 * alg: 签名算法
	 * pk: 私钥对象
	 * message: 明文
	 */
	private static byte[] sign(String alg, PrivateKey pk, byte[] message) 
	throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
		Signature s = Signature.getInstance(alg);
		s.initSign(pk);
		s.update(message);

		return s.sign();
	}

	/*
	 * 根据证书文件返回证书对象
	 */
	private static Certificate createCertificate(String certfile) 
	throws CertificateException 
	{
		try {
			InputStream in = new FileInputStream(certfile);
			CertificateFactory cf = CertificateFactory.getInstance("X.509");
			X509Certificate cert = (X509Certificate) cf.generateCertificate(in);
			try {in.close();}catch(IOException e) {}
			cert.checkValidity();
			return cert;
		}
		catch(FileNotFoundException e) {
			throw new CertificateException("证书文件不存在");
		}
	}

	/**
	* 验证签名
	* alg: 算法
	* cert: 证书对象
	* message: 明文
	* signature: 签名
	*/
	private static boolean verify(String alg, Certificate cert, 
			byte[] message, byte[] signature) {
		try {
			Signature s = Signature.getInstance(alg);
			s.initVerify(cert);
			s.update(message);
			return s.verify(signature);
		}catch(Exception e) {
			return false;
		}
	}
	
	/**
	 * 返回私钥
	 * file: 私钥文件(JKS格式)
	 * pass: 私钥文件密码 
	 */
	private static PrivateKey getPrivateKey(String file, String pass) { 
	    
	    try { 
		    
		    if (privateKey == null) { 
			    String alias = "";
				char[] password = pass.toCharArray(); 
				KeyStore ks = KeyStore.getInstance("PKCS12"); 
				ks.load(new FileInputStream(file), password);
				for (Enumeration<String> e = ks.aliases(); e.hasMoreElements();)
				{
				    alias = (String)e.nextElement(); 
					break;
				}
				privateKey = (PrivateKey) ks.getKey(alias, password);  
		    } 
		    
		    return privateKey;
		}
		catch(Exception e) {
		    e.printStackTrace();
			return null;
		}
	}
	
	private static PrivateKey privateKey = null;

	private static byte[] string2Bytes(String message) throws UnsupportedEncodingException {
		
		return message.getBytes("GBK");
	}

	private final static byte[] hex = "0123456789ABCDEF".getBytes();
	private static int parse(char c) {
		if (c >= 'a') {
			return (c - 'a' + 10) & 0x0f;
		}
		if (c >= 'A') {
			return (c - 'A' + 10) & 0x0f;
		}
		return (c - '0') & 0x0f;
	}

	private static byte[] hexStringToBytes(String hexstr) {
		byte[] b = new byte[hexstr.length() / 2];
		int j = 0;
		for (int i = 0; i < b.length; i++) {
			char c0 = hexstr.charAt(j++);
			char c1 = hexstr.charAt(j++);
			b[i] = (byte) ( (parse(c0) << 4) | parse(c1));
		}
		return b;
	}
	
	private static String bytesToHexString(byte[] b) {
		byte[] buff = new byte[2 * b.length];
		for (int i = 0; i < b.length; i++) {
			buff[2 * i] = hex[ (b[i] >> 4) & 0x0f];
			buff[2 * i + 1] = hex[b[i] & 0x0f];
		}
		return new String(buff);
	}
	
	/**
	 * 加密签名
	 * @param map
	 * @return
	 */
	public static String dosign(Map<String, Object> map){
		Map<String, Object> treeMap = new TreeMap(map);
		treeMap.remove("sign");
		String value = getCheckValue(treeMap);
		return value;
	}
	/**
	 * 加密签名
	 * @param map
	 * @param key
	 * @return
	 */
	public static String sign(Map<String, Object> map, String key){
		Map<String, Object> treeMap = new TreeMap<String, Object>(map);
		String value = getCheckValue(treeMap);
		if (StringUtils.isEmpty(value)) {
			value = "key=" + key;
		} else {
			value = value + "&key=" + key;
		}
		String signCalc = Md5(value);
		return signCalc;
	}
	public static String getCheckValue(Map<String, Object> inMap) {
		String value = "";
		for (String str : inMap.keySet()) {
			Object keyValue = inMap.get(str);
			if (keyValue instanceof List) {
				value = value + String.format("%s=%s&", str, getListValue((List) keyValue));
			} else if (keyValue instanceof Map) {
				value = value + String.format("%s=%s&", str, getCheckValue((Map<String, Object>) keyValue));
			} else {
				value = value + String.format("%s=%s&", str, keyValue);
			}
		}
		if (value.endsWith("&")) {
			value = value.substring(0, value.length() - 1);
		}
		return value;
	}

	public static String getListValue(List list) {
		if (list.size() == 0) {
			return "";
		}
		Object obj = list.get(0);
		if (obj instanceof Map) {
			String value = "";
			for (Object map : list) {
				Map<String, Object> maps = (Map<String, Object>) map;
				Map<String, Object> treeMap = new TreeMap<String, Object>(maps);
				value = value + getCheckValue(treeMap) + ",";
			}
			if (value.endsWith(",")) {
				value = value.substring(0, value.length() - 1);
			}
			return value;
		} else {
			return StringUtils.join(list, ",");
		}
	}
	
	public static String Md5(String plainText) {
		StringBuffer buf = new StringBuffer("");
		try {
			if (null != plainText && !"".equals(plainText)) {

				MessageDigest md = MessageDigest.getInstance("MD5");
				md.update(plainText.getBytes());
				byte b[] = md.digest();

				int i;
				for (int offset = 0; offset < b.length; offset++) {
					i = b[offset];
					if (i < 0)
						i += 256;
					if (i < 16)
						buf.append("0");
					buf.append(Integer.toHexString(i));
				}
			}
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return buf.toString();
	}
}
